package com.zhu.rtda.heap.constant;

import com.zhu.classfile.constant.base.ConstantFieldrefInfo;
import com.zhu.rtda.heap.Field;
import com.zhu.rtda.heap.InstanceKlass;

/**
 * 字段符号引用（对应 Go 的 FieldRef）
 *
 * 用于表示对一个字段的引用。
 * 在 class 文件中，由 CONSTANT_Fieldref_info 表示。
 */
public class FieldRef extends MemberRef {

    /**
     * 解析后的字段对象（直接引用）
     */
    private Field field;

    /**
     * 从 class 文件的 CONSTANT_Fieldref_info 创建字段符号引用
     *
     * @param cp      运行时常量池
     * @param refInfo class 文件中的 CONSTANT_Fieldref_info
     * @return 新建的字段符号引用
     */
    //对于一个字段来说它总是有类 名字 类型（描述符）需要缓存字符串
    public static FieldRef newFieldRef(RuntimeConstantPool cp, ConstantFieldrefInfo refInfo) {
        FieldRef ref = new FieldRef();
        //这个字段的符号引用属于哪个常量池
        ref.runtimeConstantPool = cp;
        ref.copyMemberRefInfo(refInfo);
        return ref;
    }

    /**
     * 获取解析后的字段对象
     */
    public Field resolvedField() {
        if (field == null) {
            resolveFieldRef();
        }
        return field;
    }

    /**
     * 解析字段符号引用
     */
    private void resolveFieldRef() {
        // 当前类c 该符号引用永远是在当前类中 但是该符号引用代表的字段或方法 不一定是当前类的字段和方法
        InstanceKlass c = runtimeConstantPool.getInstanceKlass();
        // 目标类 d
        InstanceKlass d = resolvedKlass();

        // 在类 d 中查找字段
        Field field = lookupField(d, name, descriptor);

        if (field == null) {
            throw new NoSuchFieldError("Field not found: " + name);
        }

        // 检查访问权限
        if (!field.isAccessibleTo(c)) {
            throw new IllegalAccessError(
                    "Field " + name + " is not accessible to " + d.getName()
            );
        }

        this.field = field;
    }

    /**
     * 在类及其父类、接口中查找字段
     */
    private Field lookupField(InstanceKlass klass, String name, String descriptor) {
        // 1. 当前类
        for (Field field : klass.getFields()) {
            if (field.getName().equals(name) && field.getDescriptor().equals(descriptor)) {
                return field;
            }
        }

        // 2. 接口
        for (InstanceKlass iface : klass.getInterfaces()) {
            Field field = lookupField(iface, name, descriptor);
            if (field != null) {
                return field;
            }
        }

        // 3. 父类
        if (klass.getSuperClass() != null) {
            return lookupField(klass.getSuperClass(), name, descriptor);
        }

        return null;
    }
}
